Esta es la tercera entrega de una serie de posts acerca del manejo de excepciones en Java. Algo muy importante que un buen Java developer debe conocer sobre excepciones son las llamadas checked y unchecked exceptions y el papel que juega el compilador en esto. Las checked exceptions son excepciones en donde el compilador te OBLIGA a tratarlas explícitamente para poder compilar, y las unchecked exception no (Error class incluida, más adelante explicaré eso). En otras palabras, si el compilador detecta que en tu código puede lanzar un checked exception, no te permitirá compilar hasta que lo cambies para que haga una de las siguientes dos cosas: 1) Manejas la excepción con un try/catch como lo hemos visto en posts anteriores, ó 2) usar la palabra reservada throws para que sea quien llame al método en cuestión el que la maneje, y no dentro del código original. Veamos un ejemplo.
A continuación se muestra un código en donde por medio de método estático getURLConnection obtenemos un objeto URLConnection hacia un sitio web, en este caso el sitio web de google:
import java.net.URL;
import java.net.URLConnection;
public class CheckedExceptionExample {
public static void main(String[] args) {
getURLConnection("http://www.google.com");
}
public static URLConnection getURLConnection(String websiteUrl) {
URL url = new URL(websiteUrl);
return url.openConnection();
}
}
Si intento correr este código se presenta el siguiente error:
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - unreported exception java.net.MalformedURLException; must be caught or declared to be thrown
at CheckedExceptionExample.getURLConnection(CheckedExceptionExample.java:11)
at CheckedExceptionExample.main(CheckedExceptionExample.java:7)
El código no se puede ni siquiera compilar porque hay un error en la linea 11. Lo que sucede es que en la definición del método constructor usado de la clase URL indica que podría arrojar una excepción del tipo MalformedURLException, la cual es una checked exception, por lo que es necesario tratar la excepción o arrojarla para quien llame el método getURLConnection sea quien la maneje. Nos iremos por la segunda opción, por lo que modificaremos el método getURLConnection para que sea quien llame al método getURLConnection el que se encargue de manejar la excepción, que en este caso seria el método main. Ahora nuestro código quedaría de la siguiente forma:
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class CheckedExceptionExample {
public static void main(String[] args) {
try{
getURLConnection("http://www.google.com");
}catch(MalformedURLException ex){
System.out.println("Direccion web inválida");
}
}
public static URLConnection getURLConnection(String websiteUrl) throws MalformedURLException {
URL url = new URL(websiteUrl);
return url.openConnection();
}
}
Como se puede ver, en la definición del método getURLConnection se agrego un throws MalformedURLException el cual significa que cualquier excepción del tipo MalformedURLException será lanzada a quien invoque el método, osea que en este caso seria el método main quien deberá manejar la excepción MalformedURLException si es que ocurre. Si intentamos volver a correr obtendremos lo siguiente:
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - unreported exception java.io.IOException; must be caught or declared to be thrown
at CheckedExceptionExample.getURLConnection(CheckedExceptionExample.java:18)
at CheckedExceptionExample.main(CheckedExceptionExample.java:9)
Tampoco es posible compilar porque el método openConnection de la clase URL también esta definida como que puede arrojar una checked exception, que en este caso es IOException. Volvamos a hacer otra modificación para que nuevamente esta otra checked exception sea tratada en el método main:
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class CheckedExceptionExample {
public static void main(String[] args) {
try{
getURLConnection("http://www.google.com");
}catch(MalformedURLException ex){
System.out.println("Direccion web inválida");
}catch(IOException ex){
System.out.println("Ocurrió un error de comunicación con el website");
}
}
public static URLConnection getURLConnection(String websiteUrl) throws MalformedURLException, IOException {
URL url = new URL(websiteUrl);
return url.openConnection();
}
}
Ahora si nuestro código se compilara bien y se ejecutará correctamente.
Una duda que podría surgir es: ¿Cuales excepciones son checked exception y cuales son unchecked exception? Una respuesta a eso es que el compilador te lo va a decir al momento de tratar de compilar (como en el ejemplo anterior), pero si lo que buscas es una respuesta mas formal, podríamos decir que las unchecked exception en Java con las excepciones que están jerarquicamente debajo la clase Error y la clase RuntimeException, y las demás que no entran en lo antes descrito son checked exception. El siguiente diagrama ilustra de manera simplificada lo antes mencionado, los cuadros amarillos son checked exceptions y los cuadros rosas son unchecked exceptions.
Algo a notar es que la clase Throwable esta al tope de esta jerarquía. Todos los errores y excepciones en Java heredan de ella, y únicamente los objetos que hereden de Throwable pueden ser lanzados ya sea por la JVM (Java Virtual Machine) o por nuestro código usando la palabra reservada throw. Throwable tiene dos hijitos que son las clases Exception y Error, ¿Cuál es la diferencia entre esos dos?
La clase Exception es para condiciones en donde pueda ocurrir algo anormal que pueda ser tratado, cachado, o mitigado por nuestro código, usando try/catch/finally, tal cual como lo hemos venido haciendo en ejemplos anteriores en esta serie de posts. La clase Error es para condiciones en donde puede ocurrir algo anormal que es tan serio que no valdría la pena intentar cachar o mitigar, osea que todo nuestro programa se va al carajo y no hay nada que podamos hacer para resolverlo, ejemplo de eso que se acabe la memoria a la JVM provocando un OutOfMemoryError.